import leancloud
import gevent
engine = leancloud.Engine()

@engine.define('get_data')
def get_data():
    import requests
    import re
    import datetime
    import json
    import leancloud
    response = requests.get("https://3g.dxy.cn/newh5/view/pneumonia?from=singlemessage&isappinstalled=0")
    response.encoding = response.apparent_encoding
    html = response.text
    data = list()
    data.append(dict())
    data[0]["provinceName"] = "中国"
    data[0]["continents"] = "亚洲"
    front_index = re.search("window.getStatisticsService = ", html).span()[1]
    back_index = front_index + 1
    stack = list('{')
    while stack != list():
        if html[back_index] == '{':
            stack.append(html[back_index])
        elif html[back_index] == '}':
            if stack[-1] == '{':
                stack.pop()
            else:
                stack.append(html[back_index])
        back_index += 1
    temp = json.loads(html[front_index:back_index])
    for t in ("confirmedCount", "suspectedCount", "curedCount", "deadCount", "seriousCount", "suspectedIncr"):
        if temp.get(t) != None:
            data[0][t] = temp[t]
    front_index = re.search("window.getAreaStat = ", html).span()[1]
    back_index = front_index + 1
    stack = list('[')
    while stack != list():
        if html[back_index] == '[':
            stack.append(html[back_index])
        elif html[back_index] == ']':
            if stack[-1] == '[':
                stack.pop()
            else:
                stack.append(html[back_index])
        back_index += 1
    raw_data = leancloud.Object.extend('RawData')
    date = datetime.datetime.now()
    query = raw_data.query
    query.equal_to('time', date)                                                #RawData对象先查找若没有则创建，有则更新
    query.equal_to('event_name', '2019-nCoV')
    raw_data_list = query.find()
    if raw_data_list == []:
        rd = raw_data()
        rd.set('time', date)
        rd.set('event_name', '2019-nCoV')
    data[0]["cities"] = json.loads(html[front_index:back_index])
    '''front_index = re.search("window.getListByCountryTypeService2 = ", html).span()[1]
    back_index = front_index + 1
    stack = list('[')
    while stack != list():
        if html[back_index] == '[':
            stack.append(html[back_index])
        elif html[back_index] == ']':
            if stack[-1] == '[':
                stack.pop()
            else:
                stack.append(html[back_index])
        back_index += 1
    temp = json.loads(html[front_index:back_index])
    for country in temp:
        if country["provinceName"] != "中国":
            data.append(country)'''
    rd.set('data', json.dumps(data))
    rd.save()
    
@engine.define('refresh')
def refresh():
    @gevent.spawn
    def async_convert(): 
        import json
        import leancloud
        import datetime
        import re
        area = leancloud.Object.extend("Area")
        area_data = leancloud.Object.extend("AreaData")
        raw_data = leancloud.Object.extend("RawData")
        query = raw_data.query
        query.equal_to("event_name", "2019-nCoV")
        query.descending("time")
        f = query.first()
        rawdata = json.loads(f.get("data"))

        #丁香医生"suspectedIncr"一天更新一次，且在凌晨一段时间内的原始数据无法获得"suspectedIncr",可以依据此划分当天的数据是(前一天的中间数据, 前一天的最终数据, 当天的中间数据)
        if rawdata[0].get("suspectedIncr") is None:                             #此时视为前一天的中间数据
            case = "ignore"                                                     #此时的操作为修改除"suspectedIncr"外的数据
        elif re.search("suspectedIncr", query.find()[1].get("data")) is None:    #此时视为前一天的最终数据
            case = "last"                                                       #此时的操作为修改前一天数据，修改前一天时间为23:59:59
        elif re.search("suspectedIncr", query.find()[2].get("data")) is None:    #此时视为当天的中间数据
            case = "new"                                                        #此时的操作为创建当天数据，注意将"suspectedIncr"设为0
        else:                                                                   #此时视为当天的中间数据
            case = "refresh"                                                    #此时的操作为修改当天数据，注意将"suspectedIncr"设为0

        area_save_list = list()
        area_data_save_list = list()
        for data in rawdata:
            query = area.query
            query.equal_to("event_name", "2019-nCoV")
            query.equal_to("name", data["provinceName"])
            if query.count() == 0:
                country = area()
                country.set("event_name", "2019-nCoV")
                country.set("name", data["provinceName"])
                country.set("parent_area_name", data["continents"])
                area_save_list.append(country)
            '''
            对国外数据单独处理，理由如下：
            1. 国外数据统计时间和人民日报发布时间在约北京时间0-6时之外是一样的，0-6时人民日报有延迟（可能约北京时间0-6点人民日报不上班）
            2. 人民日报发布时间和丁香医生更新时间在约北京时间23-8时之外是一样的，23-8时丁香医生有延迟（可能约北京时间23-8时丁香医生不上班）'''
            if data["provinceName"] != "中国":
                for t in ("confirmedCount", "curedCount", "deadCount"):
                    time = f.get("time")
                    query = area_data.query
                    query.equal_to("event_name", "2019-nCoV")
                    query.equal_to("area_name", data["provinceName"])
                    query.equal_to("type", t)
                    query.descending("time")
                    if query.count() == 0 or query.first().get("time").date() != time.date():
                        ad = area_data()
                        ad.set("event_name", "2019-nCoV")
                        ad.set("area_name", data["provinceName"])
                        ad.set("type", t)
                    else:
                        ad = query.first()
                    ad.set("time", time)
                    ad.set("value", data[t])
                    area_data_save_list.append(ad)
            
            else:
                for t in ("confirmedCount", "suspectedCount", "curedCount", "deadCount", "seriousCount", "suspectedIncr"):
                    if t == "suspectedIncr":
                        if case == "ignore":
                            continue
                        elif case != "last":
                            data[t] = 0
                    time = f.get("time")
                    if case == "new":
                        ad = area_data()
                        ad.set("event_name", "2019-nCoV")
                        ad.set("area_name", data["provinceName"])
                        ad.set("type", t)
                        ad.set("time", time)
                    else:
                        query = area_data.query
                        query.equal_to("event_name", "2019-nCoV")
                        query.equal_to("area_name", data["provinceName"])
                        query.descending("time")
                        query.equal_to("type", t)
                        ad = query.first()
                        if case == "last":
                            time += datetime.timedelta(days=-1)
                            ad.set("time", datetime.datetime(time.year, time.month, time.day, 23, 59, 59))
                    ad.set("value", data[t])
                    area_data_save_list.append(ad)
                for pdata in data["cities"]:
                    query = area.query
                    query.equal_to("event_name", "2019-nCoV")
                    query.equal_to("name", pdata["provinceShortName"])
                    if query.count() == 0:
                        p = area()
                        p.set("event_name", "2019-nCoV")
                        p.set("name", pdata["provinceShortName"])
                        p.set("parent_area_name", data["provinceName"])
                        area_save_list.append(p)
                    '''
                    问题1：有的省市一天发布多次报道，其中第一次应是前一天的24时
                    问题2：有的省市发布报道时间较晚
                    问题3：有的省市可能会漏报一天的疫情
                    问题4：有的省市过去没有发布报道，无法和数据库中的数据作判断
                    解决方案：分情况讨论
                    1. 库无数据：新建为一天前最终数据
                    2. 库数据时间两天前且数据无变动：新建为一天前中间数据
                    3. 库数据时间一天前且数据值有变动且为中间数据：修改为一天前最终数据
                    4. 库数据时间一天前且数据值有变动且为最终数据：新建为当天中间数据
                    5. 库数据时间为当天且数据值有变动：修改为当天新的中间数据
                    0. 其他情况不操作，case为0'''
                    case = 0
                    time = f.get("time")
                    for t in ("confirmedCount", "curedCount", "deadCount"):
                        query = area_data.query
                        query.equal_to("event_name", "2019-nCoV")
                        query.equal_to("area_name", pdata["provinceShortName"])
                        query.descending("time")
                        query.equal_to("type", t)
                        if query.count() == 0:
                            case = 1
                            break
                        elif time.date() + datetime.timedelta(days=-2) == query.first().get("time").date():
                            case = 2
                            break
                        elif query.first().get("value") != pdata[t] and \
                                time.date() + datetime.timedelta(days=-1) == query.first().get("time").date() and \
                                query.first().get("time").time() < datetime.time(23, 59, 59):
                            case = 3
                            break
                        elif query.first().get("value") != pdata[t] and \
                                time.date() + datetime.timedelta(days=-1) == query.first().get("time").date() and \
                                query.first().get("time").time() == datetime.time(23, 59, 59):
                            case = 4
                            break
                        elif query.first().get("value") != pdata[t] and \
                                time.date() == query.first().get("time").date():
                            case = 5
                            break
                    if case != 0:
                        for t in ("confirmedCount", "curedCount", "deadCount"):
                            time = f.get("time")
                            if case == 1 or case == 2 or case == 4:
                                ad = area_data()
                                ad.set("event_name", "2019-nCoV")
                                ad.set("area_name", pdata["provinceShortName"])
                                ad.set("type", t)
                            else:
                                query = area_data.query
                                query.equal_to("event_name", "2019-nCoV")
                                query.equal_to("area_name", pdata["provinceShortName"])
                                query.descending("time")
                                query.equal_to("type", t)
                                ad = query.first()
                            if case == 1 or case == 3:
                                time += datetime.timedelta(days=-1)
                                ad.set("time", datetime.datetime(time.year, time.month, time.day, 23, 59, 59))
                            elif case == 2:
                                time += datetime.timedelta(days=-1)
                                ad.set("time", datetime.datetime(time.year, time.month, time.day, 12))
                            elif case == 4:
                                ad.set("time", time)
                            ad.set("value", pdata[t])
                            area_data_save_list.append(ad)
                    
                    for cdata in pdata["cities"]:
                        if "外地" not in cdata["cityName"] and "明确" not in cdata["cityName"] and "未知" not in cdata["cityName"] and "确认" not in cdata["cityName"]:
                            if cdata["cityName"] in ["吉林", "乌海"]:
                                cdata["cityName"] += "市"
                            elif cdata["cityName"] in ["静安", "长宁", "徐汇", "奉贤", "黄浦", "虹口", "闵行", "青浦", "宝山", "嘉定", "松江", "金山", "杨浦", \
                                                        "海淀", "大兴", "西城", "昌平", "丰台", "通州", "石景山", "顺义", "东城", "门头沟", "怀柔", \
                                                        "宁河"]:
                                cdata["cityName"] += "区"
                            elif cdata["cityName"] == "浦东":
                                cdata["cityName"] += "新区"
                            elif cdata["cityName"] in ["白银市", "金昌市", "天水市", "平凉市", "吐鲁番市", "安阳市", "漯河市", "长垣县", "四平市", "澄迈县", "临高县", "陵水县", "琼海市", "东方市", "琼中县"]:
                                cdata["cityName"] = cdata["cityName"][:-1]
                            elif cdata["cityName"] in ["第八师石河子市", "第八师石河子", "石河子"]:
                                cdata["cityName"] = "第八师"
                            elif cdata["cityName"] == "胡杨河":
                                cdata["cityName"] = "第七师"
                            elif cdata["cityName"] == "宁东管委会":
                                cdata["cityName"] = "宁东"
                            elif cdata["cityName"] in ["大理", "红河", "德宏", "楚雄", "文山", "凉山"]:
                                cdata["cityName"] += "州"
                            query = area.query
                            query.equal_to("event_name", "2019-nCoV")
                            query.equal_to("name", cdata["cityName"])
                            if query.count() == 0:
                                c = area()
                                c.set("name", cdata["cityName"])
                                c.set("event_name", "2019-nCoV")
                                c.set("parent_area_name", pdata["provinceShortName"])
                                area_save_list.append(c)
                            '''
                            问题1：有的省市一天发布多次报道，其中第一次应是前一天的24时
                            问题2：有的省市发布报道时间较晚
                            问题3：有的省市可能会漏报一天的疫情
                            问题4：有的省市过去没有发布报道，无法和数据库中的数据作判断
                            解决方案：分情况讨论
                            1. 库无数据：新建为一天前最终数据
                            2. 库数据时间两天前：新建为一天前中间数据
                            3. 库数据时间一天前且数据值有变动且为中间数据：修改为一天前最终数据
                            4. 库数据时间一天前且数据值有变动且为最终数据：新建为当天中间数据
                            5. 库数据时间为当天且数据值有变动：修改为当天新的中间数据
                            0. 其他情况不操作，case为0'''
                            case = 0
                            time = f.get("time")
                            for t in ("confirmedCount", "curedCount", "deadCount"):
                                query = area_data.query
                                query.equal_to("event_name", "2019-nCoV")
                                query.equal_to("area_name", cdata["cityName"])
                                query.descending("time")
                                query.equal_to("type", t)
                                if query.count() == 0:
                                    case = 1
                                    break
                                elif time.date() + datetime.timedelta(days=-2) == query.first().get("time").date():
                                    case = 2
                                    break
                                elif query.first().get("value") != cdata[t] and \
                                        time.date() + datetime.timedelta(days=-1) == query.first().get("time").date() and \
                                        query.first().get("time").time() < datetime.time(23, 59, 59):
                                    case = 3
                                    break
                                elif query.first().get("value") != cdata[t] and \
                                        time.date() + datetime.timedelta(days=-1) == query.first().get("time").date() and \
                                        query.first().get("time").time() == datetime.time(23, 59, 59):
                                    case = 4
                                    break
                                elif query.first().get("value") != cdata[t] and \
                                        time.date() == query.first().get("time").date():
                                    case = 5
                                    break
                            if case != 0:
                                for t in ("confirmedCount", "curedCount", "deadCount"):
                                    time = f.get("time")
                                    if case == 1 or case == 2 or case == 4:
                                        ad = area_data()
                                        ad.set("event_name", "2019-nCoV")
                                        ad.set("area_name", cdata["cityName"])
                                        ad.set("type", t)
                                    else:
                                        query = area_data.query
                                        query.equal_to("event_name", "2019-nCoV")
                                        query.equal_to("area_name", cdata["cityName"])
                                        query.descending("time")
                                        query.equal_to("type", t)
                                        ad = query.first()
                                    if case == 1 or case == 3:
                                        time += datetime.timedelta(days=-1)
                                        ad.set("time", datetime.datetime(time.year, time.month, time.day, 23, 59, 59))
                                    elif case == 2:
                                        time += datetime.timedelta(days=-1)
                                        ad.set("time", datetime.datetime(time.year, time.month, time.day, 12))
                                    elif case == 4:
                                        ad.set("time", time)
                                    ad.set("value", cdata[t])
                                    area_data_save_list.append(ad)
        i = 500
        while i < len(area_save_list):
            area.save_all(area_save_list[i - 500:i])
            i += 500
        area.save_all(area_save_list[i - 500:len(area_save_list)])
        i = 500
        while i < len(area_data_save_list):
            area_data.save_all(area_data_save_list[i - 500:i])
            i += 500
        area_data.save_all(area_data_save_list[i - 500:len(area_data_save_list)])
        
@engine.define
def get_stat_data(**args):
    import datetime
    import leancloud
    import os
    import redis
    myredis = redis.from_url(os.environ.get("REDIS_URL_cache"))             #cache为leancache实例的名字，根据自己的实例来修改
    event_name = args.get('event_name')
    area_name = args.get('area_name')
    type_list = args.get('type_list')
    start = args.get('start')
    end = args.get('end')
    date_list = list()                                                          #日期列表
    value_list = [[]for i in range(len(type_list))]                             #人数列表
    dvalue_list = [[]for i in range(len(type_list))]
    AreaData = leancloud.Object.extend('AreaData')
    area_data_query = AreaData.query
    area_data_query.equal_to('event_name',event_name)
    area_data_query.equal_to('area_name',area_name)
    ds = datetime.timedelta(days=1)

    area_data_query.descending('time')
    database_end_time = area_data_query.first().get('time')
    latest_time = list()
    latest_time.append(str(database_end_time.year))
    latest_time.append('-')
    latest_time.append(str(database_end_time.month))
    latest_time.append('-')
    latest_time.append(str(database_end_time.day))
    latest_time.append(' ')
    latest_time.append(str(database_end_time.hour))
    latest_time.append(':')
    latest_time.append(str(database_end_time.minute))
    latest_time.append(':')
    latest_time.append(str(database_end_time.second))
    latest_time = ''.join(latest_time)

    latest_date = list()
    latest_date.append(str(database_end_time.year))
    latest_date.append('-')
    latest_date.append(str(database_end_time.month))
    latest_date.append('-')
    latest_date.append(str(database_end_time.day))
    latest_date = ''.join(latest_date)

    area_data_query.ascending('time')
    database_start_time = area_data_query.first().get('time')
    database_start_date = datetime.datetime(database_start_time.year,database_start_time.month,database_start_time.day)
    database_start = list()
    database_start.append(str(database_start_date.year))
    database_start.append('-')
    database_start.append(str(database_start_date.month))
    database_start.append('-')
    database_start.append(str(database_start_date.day))
    database_start = ''.join(database_start)

    if start != None and start > database_start:
        start = start.split('-')
        tmp_start_date = datetime.datetime(int(start[0]),int(start[1]),int(start[2])) - ds
    else:
        tmp_start_date = database_start_date

    if end != None:
        end = end.split('-')
        tmp_end_date = datetime.datetime(int(end[0]),int(end[1]),int(end[2])) + ds
        area_data_query.greater_than_or_equal_to('time',tmp_start_date)
        area_data_query.less_than('time',tmp_end_date)
    else:
        area_data_query.greater_than_or_equal_to('time',tmp_start_date)


    if area_data_query.count() != 0:
        current_area_data_query = area_data_query
        for i in range(len(type_list)):
            current_area_data_query.equal_to('type',type_list[i])
            area_data = current_area_data_query.find()
            if type_list[i] != 'suspectedCount':
                current_date = tmp_start_date
                for j in range(len(area_data)):
                    if i == 0:
                        tmp_date = list()
                        tmp_date.append(str(current_date.year))
                        tmp_date.append('-')
                        tmp_date.append(str(current_date.month))
                        tmp_date.append('-')
                        tmp_date.append(str(current_date.day))
                        tmp_date = ''.join(tmp_date)
                        date_list.append(tmp_date)
                    mykey = event_name + area_name + type_list[i] + tmp_date
                    value = myredis.get(mykey)
                    if value == None and tmp_date != latest_date:
                        value = area_data[j].get('value')
                        value = str(value)
                        myredis.set(mykey,value)
                    elif value == None and tmp_date == latest_date:
                        value = area_data[j].get('value')
                        value = str(value)
                    value = float(value)
                    value_list[i].append(value)
                    if j > 0:
                        dvalue_list[i].append(value_list[i][j]-value_list[i][j-1])
                    current_date += ds
                if i == 0:
                    del date_list[0]
                del value_list[i][0]
            else:
                current_date = tmp_start_date + ds
                tmp_area_data_query = area_data_query
                tmp_area_data_query.equal_to('type','suspectedIncr')
                tmp_area_data = tmp_area_data_query.find()
                del area_data[0]
                del tmp_area_data[0]
                for j in range(len(area_data)):
                    if i == 0:
                        tmp_date = list()
                        tmp_date.append(str(current_date.year))
                        tmp_date.append('-')
                        tmp_date.append(str(current_date.month))
                        tmp_date.append('-')
                        tmp_date.append(str(current_date.day))
                        tmp_date = ''.join(tmp_date)
                        date_list.append(tmp_date)
                    mykey = event_name + area_name + type_list[i] + tmp_date
                    tmp_key = event_name + area_name + 'suspectedIncr' + tmp_date
                    value = myredis.get(mykey)
                    tmp_value = myredis.get(tmp_key)
                    if value == None and tmp_date != latest_date:
                        value = area_data[j].get('value')
                        value = str(value)
                        myredis.set(mykey,value)
                    elif value == None and tmp_date == latest_date:
                        value = area_data[j].get('value')
                        value = str(value)
                    value = float(value)
                    value_list[i].append(value)
                    if tmp_value == None and tmp_date != latest_date:
                        tmp_value = tmp_area_data[j].get('value')
                        tmp_value = str(tmp_value)
                        myredis.set(tmp_key,tmp_value)
                    elif tmp_value == None and tmp_date == latest_date:
                        tmp_value = tmp_area_data[j].get('value')
                        tmp_value = str(tmp_value)
                    tmp_value = float(tmp_value)
                    dvalue_list[i].append(tmp_value)
                    current_date += ds
            current_area_data_query = area_data_query
    else:
        return date_list,value_list,dvalue_list,latest_time
    return date_list,value_list,dvalue_list,latest_time

@engine.define
def get_areas(**args):
    import datetime
    import leancloud
    import os
    import redis
    myredis = redis.from_url(os.environ.get("REDIS_URL_cache"))                 #cache为leancache实例的名字，根据自己的实例来修改
    event_name = args.get('event_name')
    area_name = args.get('area_name')
    name_list = list()
    Area = leancloud.Object.extend('Area')
    area_query = Area.query
    area_query.equal_to('event_name',event_name)
    area_query.equal_to('parent_area_name',area_name)
    area_query.matched('name','^(?!.*人员)')
    if area_query.count() != 0:
        area = area_query.find()
        mykey = event_name + area_name + 'areas'
        l = myredis.llen(mykey)
        if l == 0:
            for i in range(len(area)):
                myredis.rpush(mykey,area[i].get('name').encode('UTF-8'))
                name_list.append(myredis.lindex(mykey,i).decode('UTF-8'))
        else:
            for i in range(l):
                name_list.append(myredis.lindex(mykey,i).decode('UTF-8'))
    else:
        return name_list
    return name_list